package emby.lang;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Hashtable;

import emby.err.InvalidArgumentError;
import emby.err.NoConstructorException;
import emby.err.RuntimeError;
import emby.err.TypeError;
import emby.overload.OverloadCallable;
import emby.overload.OverloadDot;
import emby.util.ReflectionHelp;

public class JavaClassWrapper extends EmbyObject implements OverloadCallable, OverloadDot,Serializable{
	
	private static final long serialVersionUID = -7473384449728661910L;
	private static final ArrayList<String> DEFAULT_IMPORTS = getDefaultImports();
	public static ArrayList<String> getDefaultImports(){
		ArrayList<String> def = new ArrayList<String>();
		def.add("");
		def.add(formatImport("java.lang"));
		return def;
	}
	
	public static String formatImport(String imp){
		imp = imp.trim();
		if( imp.endsWith(".")){
			return imp;
		}
		if( imp.endsWith(".*")){
			imp = imp.substring(0, imp.length()-2);
		}
		return imp+".";
	}

	String className;
	JavaConstructorWrapper construct;
	Class myclass;
	Hashtable<String, Object> methodCache;
	
	public JavaClassWrapper(String classname){
		this(-1, classname);
	}
	
	public JavaClassWrapper(int line, String classname){
		this(line, classname, DEFAULT_IMPORTS);
	}
	
	public JavaClassWrapper(int line, String classname, ArrayList<String> imports){
		/*
		for(int i=0; i<imports.size(); i++){
			try {
				String fullname = imports.get(i)+classname;
				myclass= Class.forName(fullname); //attempt to find class
				this.className = fullname;
				break;
			} catch (ClassNotFoundException e) {
				if( i>= imports.size()-1){ //no more imports left to scan
					throw new RuntimeError(line, "Class "+classname+" not found.");	
				}
			}
		}
		*/
		this(ReflectionHelp.findClass(line, classname, imports));
	}
	
	public JavaClassWrapper(Class myclass){
		this.myclass = myclass;
		this.className = myclass.getCanonicalName();
		this.methodCache = new Hashtable<String,Object>();
		try{
			construct = new JavaConstructorWrapper(myclass);
		}catch(NoConstructorException e){
			construct = null;
		}
	}
	
	@Override
	public Object execute(int lineNumber, Scope s, Object... args) {
		if( construct==null){
			throw new NoConstructorException(lineNumber, className);
		}
		return construct.execute(lineNumber, s, args);
	}

	public String toString(){
		return "JavaClassWrapper:"+className;
	}

	@Override
	public Object get_dot(int line, String attribute) {
		if( methodCache.contains(attribute)){
			return methodCache.get(attribute);
		}
		Object wrapper = ReflectionHelp.getFieldValueOrMethodWrapper(line, myclass, attribute, null);	
		methodCache.put(attribute, wrapper);
		return wrapper;
	}

	@Override
	public void set_dot(int line, String attribute, Object new_value) {
		throw new TypeError(line,"Cannot assign values to a class wrapper.");
	}
}
